package BeanTest;

import org.springframework.web.SpringServletContainerInitializer;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.AbstractContextLoaderInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.HttpServletBean;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;

import javax.servlet.GenericServlet;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import javax.servlet.http.HttpServlet;



@EnableWebMvc //启用SpringMVC功能，修改SpringMVC底层行为就会很方便只需要实现WebMvcConfigurer即可
// 不要这个注解回到以前默认模式，所有组件DispatcherServlet初始化的时候没有，直接用配置文件中指定的默认的组件
//  没有预留扩展接口，需要我们自己重新替换
//1、WebMvcConfigurer+@EnableWebMvc 定制和扩展SpringMVc功能
//2、@EnableWebMvc导入的类会加入SpringMVC的很多核心组件，拥有默认功能
//3、这些默认功能在扩展的时候都是留给接口 WebMvcConfiqurer(访问者，拿到真正的内容进行修改）可以介入
//4、MeiNvViewResolver+InternalResourceViewResolver
//5、@EnableWebMvc开启了SpringMVC === <mvc:annotation-driven/>，即使是以前自己也要配置默认视图解析器
public class SpringMVC implements WebMvcConfigurer {

//1 另外一种
//@Configuration MvcExtendConfiguration extends DelegatingWebMvcConfiguration
//1、拿到父类@Bean的方法，还是给容器中放了组件
//2、只是为了实现一个效果，就是让 DelegatingWebMvcConfiguration 的类或者子类放在容器
//3、只要这个 DelegatingWebMvcConfiguration 生效，他从容器中拿所有的configure进打
//4、两种方式把 DelegatingWebMvcconfiguration
// 		1)、随便在哪个配置类位置加 @EnableWebMvc，然后只需要给 容器中放 WebMvcConfigurer即可
// 		2)、自己写一个配置类(在容器中)来继承DelegatingWebMvcConfiguration，然后只需要给 容器中放 WebMvcConfigurer即可
// 		3)、自己写一个配置类(在容器中)来继承WebMvcConfigurationSupport,我们只能实现模板方法

//这个DelegatingWebMvcConfiguration的特效就是:
//	1、给容器中放组件比如:HandlerMapping
// 	2、HandlerMapping的关键环节留给模板方法
// 	3、DelegatingWebMvcConfiguration拿到所有的 WebMvcConfigurer，在模板方法实现的时候由WebMvcconfiqurer进行定制

	@Override//配置视图解析器，升级这个组件的功能
	public void configureViewResolvers(ViewResolverRegistry registry) {

	}



	//SpringMVC源码解析--->QuickApp
	/**
	 * @see SpringServletContainerInitializer
	 * 总述：
	 * 不用加 @Component注解，因为 tomcat 通过 SPI 会把实现这个接口 ServletContainerInitialize 的类（SpringServletContainerInitializer）加载进来
	 * SpringServletContainerInitializer 通过 @HandlesTypes(WebApplicationInitializer.class) 注解来筛选 所有实现了 WebApplicationInitializer接口的类，对其创建对象并调用onstartup方法
	 *
	 * 只要写了这个，相当于配置了 SpringMVC 的 DispatcherServlet
	 * 1、Tomcat一启动就加载他
	 * 		1)、创建了容器、制定了配置类(所有ioc、aop等spring的功能就ok)
	 * 		2)、注册一个Servlet：DispatcherServlet;
	 * 		3)、以后所有的请求都交给了 DispatcherServlet：
	 * 	 效果，访间Tomcat部署的这个web应用下的所有请求都会被DispatcherServlet处理
	 * 	 DispatcherServlet就会进入强大的基于注解的mvc处理流程(@GetMapping)
	 */
	public class MyWebAppliicationInitializer implements WebApplicationInitializer {

		@Override
		public void onStartup(javax.servlet.ServletContext servletContext) throws ServletException {

			//（一）准备空的IOC容器，这时候容器还没有进行刷新
			AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
			context.register(AppConfig.class);	//注册主配置类

			//（二）创建 DispatcherServlet 对象，传入了ioc容器，并注册到了ServeltContext中（Tomcat）
			//1. DispatcherServlet 是一个 Servelt， Tomcat在启动的时候会为每一个Servelt创建对象
			//2. Tomcat 自然会给 DispatcherServlet（tomcat保证其是一个单实例） 创建对象
			//3. DispatcherServlet 是 Servelt，一定会初始化，整个初始化调用过来以后就会启动IOC容器	--> 就要去找初始化init方法
			/**
			 * @see Servlet
			 *	Servelt的规范-->生命周期
			 * 1.Servelt创建对象
			 * 2.Servelt调用init初始化
			 * 3.每次请求过来调用service处理
			 * 4.tomcat停止的时候，调用destory销毁
			 */
			//（三）截至到目前，容器还没有初始化，下面配置了 DispatcherServlet，利用 servlet 的初始化机制
			DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
			ServletRegistration.Dynamic registration = servletContext.addServlet("app", dispatcherServlet);	//利用Servlet规范添加我的Servelt
			registration.setLoadOnStartup(1);
			registration.addMapping("/");	//指定映射
			//启动了容器？---> 上面的servlet 添加到 servletContext后，tomcat会对 DispatcherServlet 初始化 ---> HttpServletBean 的 init方法

			/**
			 * @see Servlet  		顶级父类 --- init()是空实现
			 * @see GenericServlet    init()是空实现
			 * @see HttpServlet    只是把原来Servelt的serice方法拆分成doGet、doPost等，但 init() 还是空实现
			 * -------------------------------------------下面的是Spring实现的
			 * @see HttpServletBean        重写了 init() 方法，同时留给子类可实现的方法 initServletBean
			 * @see FrameworkServlet    子类在 initServletBean 进行容器的初始化
			 * @see DispatcherServlet
			 */
		}
	}


	/**
	 * @see WebApplicationInitializer
	 * @see AbstractContextLoaderInitializer
	 * @see AbstractDispatcherServletInitializer
	 * @see AbstractAnnotationConfigDispatcherServletInitializer
	 */
	public class QuickApp extends AbstractAnnotationConfigDispatcherServletInitializer{

		//根容器的配置（Spring的配置文件===Spirng的配置类）  SpringConfig（父） 不扫描 Controller组件
		@Override
		protected Class<?>[] getRootConfigClasses() {
			return new Class[0];
		}

		//web容器的配置（SpringMvc的配置文件===SpringMVC的配置类）		SpringMVCConfig（子） 只扫描 Controller组件
		@Override
		protected Class<?>[] getServletConfigClasses() {
			return new Class[0];
		}

		//Servlet的映射，DispatcherServlet的映射路径
		@Override
		protected String[] getServletMappings() {
			return new String[0];
		}
	}


	/**
	 * SpringMVC父子容器的启动过程
	 * 		1.Tomcat启动，扫描所有的 WebApplicationInitializer（通过SPI，调用 SpringServletContainerInitializer 的 onstartup 方法，在这个方法里面又会调用所有实现类的onstartup方法）
	 * 		2.QuickApp 是 WebApplicationInitializer 子类，调用onstartup方法
	 * 		3.QuickApp.onStratup()--->调用的是 AbstractDispatcherServletInitializer 的 onstartup方法
	 *
	 * 	注意事项：	WebApplicationInitializer是引导我们先按照事先指定的配置类把父子容器创建出来，但还没有进行refresh初始化。
	 *
	 * 		“应用启动监听器回调”
	 * 		4.@3435 在web应用启动完成的时候，Tomcat触发监听器钩子启动根容器	           		--->ContextLoaderListener#contextInitialized()
	 * 				所有页面逻辑组件功能AOP、事务、IOC、自动装配、Service完成
	 *
	 * 		“Servlet初始化回调”
	 * 		5.@3476 会在Tomcat启动了以后，tomcat调用 DispatcherServlet 的init初始化  		--->DispatcherServlet#init()，实际是调用HttpServletBean的init()
	 * 				Controller进行创建对象，自动装配，当前没有还会去父容器找(这时候父容器Service已经初始化完成了，所以Controller采用@Autowired自动装配才能找到)
	 */


	/**
	 * SpringMVC处理请求
	 *
	 * 		八大核心组件：
	 * 				1.只要不满意八大组件的任何一个，自己实现指定的接口，并放在容器中，SpringMVC启动就会加载我们自己的(因为spring会先从容器中拿，如果没有拿到就采取默认的)
	 * 				2.九大组件在何时进行初始化：	【Spring的事件驱动-->即容器refresh后发布事件，FrameworkServlet回调onRefresh方法】
	 * 					Tomcat启动 --> 触发 DispatcherServlet 的初始化 --> 容器初始化全部结束后，容器会发送Spring的事件 -->  感知到容器准备好了的事件 ---> 初始化九大组件
	 * 						---> 底层是SourceFilteringListener ，把事件回调到 DispatcherServlet 的 onRefresh方法 ---> initStrategies(通过静态代码块初始化默认策略)
	 * 				3.九大组件除了文件上传组件，都会有默认值策略。文件上传功能，需要我们自己导入相关包并进行配置
	 *
	 *
	 * 		@see Servlet  		顶级父类 --- Service()
	 * 		@see GenericServlet	abstract void service()
	 * 		@see HttpServlet 	只是把原来Servelt的serice方法拆分成doGet()、doPost()
	 * 		--------------------------------------------------------------------------下面的是Spring实现的
	 * 		@see HttpServletBean
	 * 		@see FrameworkServlet	processRequest
	 * 		@see DispatcherServlet	doService--->doDispatch
	 *
	 * 		Spring默认提供的3个HandlerMapping：
	 * 	  		BeanNameUrlHandelerMapping：扫描所有名字以/开头的bean，注册到url映射中
	 * 	  		RequestMappingHandlerMapping：扫描所有@controller有方法标注了@RequestMapping注解的，注册到url映射中
	 * 	 		RouterFunctionMapping：webflux应用
	 *
	 * 		HandlerMapping 的 registry中如何有数据 ? ---> registry中存的是哪一个请求应该用哪一个方法(Controller)去处理的映射关系
	 * 		1.DispatcherServlet创建对象后，Tomcat调用初始化回调钩子initServletBean()
	 * 		2.最终容器启动完成，Spring发送事件，回调到DispatcherServet的onRefresh()
	 * 		3.onRefresh初始化九大组件
	 * 		4.handlerMapping初始化
	 * 			4.1、创建所有配置中指定的handlerMapping对象
	 * 			4.2、启动了createBean来创建HandlerMapping的流程
	 * 			4.3、RequestMappingHandlerMapping对象创建完成以后开始了初始化流程
	 * 			4.4、因为RequestMappingHandlerMapping实现了InitializingBean生命周期接口
	 * 			4.5、调用了RequestMappingHandlerMapping的初始化方法afterPropertiesSet
	 * 			4.6、拿到Web容器(子容器)中的所有组件，挨个处理，判断是否有 @Controller 或者 @RequestMapping
	 * 			4.7、把分析到的RequestMapping信息放到HandlerMapping的registry中
	 *
	 * 	注意事项：所有组件可能都会用的功能，可以用后置处理器BeanPostprocessor来做
	 * 			单组件增强的，最好用生命周期InitializingBean来做
	 *
	 *
	 *
	 * 	HandlerAdpter
	 * 		Spring默认提供的4个HandlerAdpter
	 * 			1.HttpRequestHandlerAdapter，判断是否是HttpRequestHandler接口
	 *			2.SimpleControllerHandlerAdapter，判断是否实现Controller接口
	 *			前两个adapter都是直接进行调用我们自己实现接口的handleRequest方法
	 *			3.RequestMappingHandlerAdapter，判断当前是不是一个HandlerMethod
	 *		HandlerAdapter 如何有数据？(跟HandlerMapping类似，都是利用InitializingBean生命周期)
	 *		1.DispatcherServlet创建对象后，Tomcat调用初始化回调钩子initServletBean()
	 * 	  	2.最终容器启动完成，Spring发送事件，回调到DispatcherServet的onRefresh()
	 * 	 	3.onRefresh初始化九大组件
	 * 	  	4.HandlerAdapter 初始化
	 * 	 		4.1、创建所有配置中指定的 HandlerAdapter 对象
	 * 	  		4.2、启动了createBean来创建 HandlerAdapter 的流程
	 * 	  		4.3、RequestMappingHandlerAdapter对象创建完成以后开始了初始化流程
	 * 	  		4.4、因为RequestMappingHandlerAdapter实现了InitializingBean生命周期接口
	 * 	  		4.5、调用了RequestMappingHandlerAdapter的初始化方法afterPropertiesSet
	 * 	  		4.6、初始化了ControllerAdvice相关功能，准备了所有的参数解析器和返回值处理器
	 * 	  		4.7、参数解析器和返回值处理器等放到组合对象中保存起来
	 */
}
